package com.cloudpolice.weixin.controller;

import com.cloudpolice.weixin.feignInterface.PersonInterFace;
import com.cloudpolice.weixin.model.*;
import com.cloudpolice.weixin.model.InPutParam.WechatInsertInParam;
import com.cloudpolice.weixin.model.InPutParam.WechatLogInsertInParam;
import com.cloudpolice.weixin.model.InPutParam.WechatSelectInParam;
import com.cloudpolice.weixin.service.WechatLogService;
import com.cloudpolice.weixin.service.WechatService;
import com.cloudpolice.weixin.unit.GetPropertiesClass;
import com.cloudpolice.weixin.unit.RedisOperator;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.gson.Gson;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lh.model.ResultVO;
import lh.model.ResultVOPage;
import lh.units.ResultStruct;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.*;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.List;
import java.security.AlgorithmParameters;
import java.security.Security;


@RestController
@EnableEurekaClient
@RequestMapping("/wechatController")
@Api(value = "微信小程序聚合层", description = "微信小程序通用方法")
public class WechatController {
    @Autowired
    WechatService wechatService;
    @Autowired
    GetPropertiesClass getPropertiesClass;
    @Autowired
    RedisOperator redisOperator;
    @Autowired
    WechatLogService wechatLogService;
    @Autowired
    PersonInterFace personInterFace;
    @Autowired
    Gson gson;

    @ApiOperation(value = "得到小程序授权后的本机手机号", notes = "微信小程序通用接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "encryptedData", value = "密钥", dataType = "String", required = true),
            @ApiImplicitParam(name = "iv", value = "iv", dataType = "String", required = true),
            @ApiImplicitParam(name = "sessionKey", value = "sessionKey", dataType = "String", required = true),
    })
    @PostMapping("getMyMobile")
    public ResultVO getMyMobile(@RequestParam(value = "encryptedData") String encryptedData
            , @RequestParam(value = "iv") String iv
            , @RequestParam(value = "sessionKey") String sessionKey) throws NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidParameterSpecException {
        sessionKey = sessionKey.replace(" ", "+");
        encryptedData = encryptedData.replace(" ", "+");
        iv = iv.replace(" ", "+");
        final String phoneNumber = getPhoneNumber(sessionKey, encryptedData, iv);
        final PhoneModel phoneModel = gson.fromJson(phoneNumber, PhoneModel.class);
        return phoneModel == null ? ResultStruct.error(ResultVO.class) : ResultStruct.success(phoneModel);
    }

    private String getPhoneNumber(String sessionKey, String encryptedData, String iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
        byte[] keyByte = Base64.decode(sessionKey);
        byte[] dataByte = Base64.decode(encryptedData);
        byte[] ivByte = Base64.decode(iv);
        if (keyByte == null) {
            return null;
        }
        int base = 16;
        if (keyByte.length % base != 0) {
            int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
            keyByte = temp;
        }
        // 初始化
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
        AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
        parameters.init(new IvParameterSpec(ivByte));
        cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
        byte[] resultByte = cipher.doFinal(dataByte);
        if (null != resultByte && resultByte.length > 0) {
            return new String(resultByte, "UTF-8");
        } else
            return null;
    }

    /**
     * 根据个人ID与人员类型，得到微信小程序信息，方法ID：SE2020022016332746695777AAF9BE2
     *
     * @param personId   表sys_Wechat,字段名personId:业务系统中的个人编号
     * @param personType 表sys_Wechat,字段名personType:人员类型（0：居民，1：物业人员，2：警务人员)
     * @param page       当前页数
     * @param limit      每页条数
     * @return 微信小程序信息
     */
    @ApiOperation(value = "根据个人ID与人员类型，得到微信小程序信息,方法ID：SE2020022016332746695777AAF9BE2", notes = "微信小程序信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "personId", value = "业务系统中的个人编号", dataType = "String", required = true),
            @ApiImplicitParam(name = "personType", value = "人员类型（0：居民，1：物业人员，2：警务人员)", dataType = "String", required = true),
            @ApiImplicitParam(name = "page", value = "当前页数", dataType = "String"),
            @ApiImplicitParam(name = "limit", value = "每页条数", dataType = "String")
    })
    @PostMapping("/SelectWeiXinInfoByPersonId")
    public ResultVOPage SelectWeiXinInfoByPersonId(@RequestParam(value = "personId") String personId
            , @RequestParam(value = "personType", defaultValue = "0") int personType
            , @RequestParam(value = "page", defaultValue = "1") int page
            , @RequestParam(value = "limit", defaultValue = "10") int limit) {
        personId = personId == null ? personId : personId.trim();

        WechatSelectInParam wechatSelectInParam = new WechatSelectInParam();
        wechatSelectInParam.setPersonId(personId);
        wechatSelectInParam.setPersonType(personType);

        PageHelper.startPage(page, limit);
        List<WechatModel> wechatModels = wechatService.SelectWeiXinInfoByPersonId(wechatSelectInParam);
        PageInfo pageInfo = new PageInfo<>(wechatModels, limit);
        return ResultStruct.successPage(wechatModels, pageInfo.getPageNum(), pageInfo.getPageSize(), pageInfo.getTotal());
    }

    /**
     * 微信小程序登录授权
     *
     * @param appId appId
     * @param code  用户code
     * @return 导航栏结构化数据
     */
    @ApiOperation(value = "微信小程序登录授权", notes = "导航栏结构化数据")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appId", value = "appId", dataType = "String", required = true)
            , @ApiImplicitParam(name = "code", value = "用户code", dataType = "String", required = true)
    })
    @PostMapping("/loginAll")
    public String loginAll(@RequestParam(value = "appId") String appId,
                           @RequestParam(value = "code") String code) throws URISyntaxException, IOException {
        if (appId == null) {
            return "appId，不能为空";
        }
        appId = appId.trim();
        final String sessionKeyAndOpenId = redisOperator.getSessionKeyAndOpenId(appId
                , code);
        if (sessionKeyAndOpenId != null && !sessionKeyAndOpenId.isEmpty()) {
            final SessionAndOpenModel sessionAndOpenModel = gson.fromJson(sessionKeyAndOpenId, SessionAndOpenModel.class);
            LogResult logResult = new LogResult();
            logResult.setOpenid(sessionAndOpenModel.getOpenId());
            logResult.setSession_key(sessionAndOpenModel.getSessionKey());
            insertAppIdAndOpenId(appId, logResult);
            return sessionKeyAndOpenId;
        }
        CloseableHttpClient httpclient = HttpClients.createDefault();
        String resultString = null;
        CloseableHttpResponse response;
        String url = String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=" +
                        "%s&grant_type=%s"
                , appId
                , getPropertiesClass.getAppSecret()
                , code
                , getPropertiesClass.getGrantType());
        URIBuilder builder = new URIBuilder(url);
        URI uri = builder.build();
        HttpGet httpGet = new HttpGet(uri);
        response = httpclient.execute(httpGet);
        // 判断返回状态是否为200
        if (response.getStatusLine().getStatusCode() == 200) {
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
            if (resultString.indexOf("errcode") < 0) {
                final LogResult logResult = gson.fromJson(resultString, LogResult.class);
                redisOperator.saveSessionKeyAndOpenId(appId
                        , code
                        , logResult.getOpenid()
                        , logResult.getSession_key()
                        , getPropertiesClass.getExpirationTime());
                insertAppIdAndOpenId(appId, logResult);
            }
            System.out.println(resultString);
        }
        return resultString;
    }

    private void insertAppIdAndOpenId(String appId, LogResult logResult) {
        WechatInsertInParam wechatInsertInParam = new WechatInsertInParam();
        wechatInsertInParam.setAppId(appId);
        wechatInsertInParam.setOpenId(logResult.getOpenid());
        wechatService.insertIntoWechat(wechatInsertInParam);
        WechatLogInsertInParam wechatLogInsertInParam = new WechatLogInsertInParam();
        wechatLogInsertInParam.setAppId(appId);
        wechatLogInsertInParam.setOpenId(logResult.getOpenid());
        wechatLogService.insertLoginLog(wechatLogInsertInParam);
    }

    /**
     * 根据个人ID与人员类型，得到微信小程序信息，方法ID：IN202002202208171223D22839167C7
     *
     * @param appId        appId
     * @param openId       openId
     * @param nickName     微信昵称
     * @param headPicture  头像链接
     * @param personWXCode 个人微信号
     * @param personId     业务系统中的个人编号
     * @param personType   人员类型（0：居民，1：物业人员，2：警务人员)
     * @return 微信小程序信息
     */
    @ApiOperation(value = "根据个人ID与人员类型，得到微信小程序信息,方法ID：IN202002202208171223D22839167C7", notes = "微信小程序信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appId", value = "appId", dataType = "String", required = true)
            , @ApiImplicitParam(name = "openId", value = "openId", dataType = "String", required = true)
            , @ApiImplicitParam(name = "nickName", value = "微信昵称", dataType = "String")
            , @ApiImplicitParam(name = "headPicture", value = "头像链接", dataType = "String")
            , @ApiImplicitParam(name = "personWXCode", value = "个人手机号", dataType = "String", required = true)
            , @ApiImplicitParam(name = "personId", value = "业务系统中的个人编号", dataType = "String")
            , @ApiImplicitParam(name = "personType", value = "人员类型（0：居民，1：物业人员，2：警务人员)", dataType = "String", defaultValue = "0", required = true)
            , @ApiImplicitParam(name = "sameSign", value = "是否支持不同的手机绑定同一个人微信号", dataType = "Boolean", defaultValue = "1",required = true)
    })
    @PostMapping("/insertIntoWechat")
    public ResultVO insertIntoWechat(@RequestParam(value = "appId") String appId
            , @RequestParam(value = "openId") String openId
            , @RequestParam(value = "nickName", required = false) String nickName
            , @RequestParam(value = "headPicture", required = false) String headPicture
            , @RequestParam(value = "personWXCode") String personWXCode
            , @RequestParam(value = "personId", required = false) String personId
            , @RequestParam(value = "personType", defaultValue = "0") int personType
            , @RequestParam(value = "sameSign", defaultValue = "1") boolean sameSign
    ) {
        appId = appId == null ? appId : appId.trim();
        openId = openId == null ? openId : openId.trim();
        nickName = nickName == null ? nickName : nickName.trim();
        headPicture = headPicture == null ? headPicture : headPicture.trim();
        personWXCode = personWXCode == null ? personWXCode : personWXCode.trim();
        personId = personId == null ? personId : personId.trim();

        WechatInsertInParam wechatInsertInParam = new WechatInsertInParam();
        wechatInsertInParam.setAppId(appId);
        wechatInsertInParam.setOpenId(openId);
        if (personWXCode != null) {
            wechatInsertInParam.setPersonWXCode(personWXCode);
            final WechatInsertInParam wechatInsertInParam1 = wechatService.SelectWeiXinInfoByMobile(wechatInsertInParam);
            if (wechatInsertInParam1 != null) {
                final String personWXCode1 = wechatInsertInParam1.getPersonWXCode();
                if (sameSign && !personWXCode.equals(personWXCode1))
                    return ResultStruct.error(String.format("此手机号[%s]，与上次绑定的手机号不同，请重新绑定！", personWXCode)
                            , ResultVO.class);
                else {
                    personId = wechatInsertInParam1.getPersonId();
                }
            }
        }
        if (personId == null) {
            final ResultVO havePersonByMobileAndCode = personInterFace.getHavePersonByMobileAndCode(personWXCode, null, null, null);
            if (havePersonByMobileAndCode.getCode() == 0) {
                final PersonModel personModel = gson.fromJson(havePersonByMobileAndCode.getData().toString(), PersonModel.class);
                if (personModel != null) {
                    personId = personModel.getId();
                    personType = personModel.getPersonType();
                }
            }
        }
        if (personId == null) {
            return ResultStruct.error(String.format("根据此手机号[%s]，未找到对应的人！"
                    , personWXCode)
                    , ResultVO.class);
        }
        wechatInsertInParam.setPersonWXCode(personWXCode);
        wechatInsertInParam.setPersonType(personType);
        wechatInsertInParam.setPersonId(personId);
        wechatInsertInParam.setPersonType(personType);
        wechatInsertInParam.setNickName(nickName);
        wechatInsertInParam.setHeadPicture(headPicture);
        final int i = wechatService.insertIntoWechat(wechatInsertInParam);
        if (i > 0) {
            PersonModel personModel = new PersonModel();
            personModel.setId(personId);
            personModel.setPersonType(personType);
            return ResultStruct.success(personModel);
        } else {
            return ResultStruct.error("操作失败", ResultVO.class);
        }
    }

    /**
     * 根据个人ID与与OpenId关联
     *
     * @param appId      appId
     * @param openId     openId
     * @param personId   业务系统中的个人编号
     * @param personType 人员类型（0：居民，1：物业人员，2：警务人员)
     * @return 微信小程序信息
     */
    @ApiOperation(value = "根据个人ID与人员类型，得到微信小程序信息,方法ID：IN202002202208171223D22839167C7", notes = "微信小程序信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appId", value = "appId", dataType = "String", required = true)
            , @ApiImplicitParam(name = "openId", value = "openId", dataType = "String", required = true)
            , @ApiImplicitParam(name = "personId", value = "业务系统中的个人编号", dataType = "String", required = true)
            , @ApiImplicitParam(name = "personType", value = "人员类型（0：居民，1：物业人员，2：警务人员)", dataType = "String", defaultValue = "0", required = true)
    })
    @PostMapping("/updateWechatPerson")
    public ResultVO updateWechatPerson(@RequestParam(value = "appId") String appId
            , @RequestParam(value = "openId") String openId
            , @RequestParam(value = "personId") String personId
            , @RequestParam(value = "personType") int personType) {
        appId = appId == null ? appId : appId.trim();
        openId = openId == null ? openId : openId.trim();
        personId = personId == null ? personId : personId.trim();

        WechatInsertInParam wechatInsertInParam = new WechatInsertInParam();
        wechatInsertInParam.setAppId(appId);
        wechatInsertInParam.setOpenId(openId);
        wechatInsertInParam.setPersonId(personId);
        wechatInsertInParam.setPersonType(personType);
        int resultCount = wechatService.updateWechatPerson(wechatInsertInParam);
        if (resultCount > 0)
            return ResultStruct.success("关联成功", resultCount);
        else
            return ResultStruct.error("关联失败", ResultVO.class, int.class);
    }
}
